package com.kexio.gen.service.impl;

import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.springframework.stereotype.Service;

import com.kexio.gen.domain.entity.GenTable;
import com.kexio.gen.domain.entity.GenTableColumn;
import com.kexio.gen.service.CodeGenService;

import cn.hutool.core.io.FileUtil;

@Service
public class CodeGenServiceImpl implements CodeGenService {

    private freemarker.template.Configuration cfg;

    private freemarker.template.Configuration cfg() {
        if (cfg == null) {
            cfg = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_32);
            cfg.setDefaultEncoding("UTF-8");
            // 注意：第二个参数不能以 "/" 开头，应为类路径下的相对目录
            cfg.setClassLoaderForTemplateLoading(CodeGenServiceImpl.class.getClassLoader(), "templates/gen");
            cfg.setTemplateUpdateDelayMilliseconds(0);
            cfg.setLogTemplateExceptions(false);
            // 生成代码场景：布尔打印使用计算机语言格式 true/false
            cfg.setBooleanFormat("c");
            // 避免 MyBatis "#{param}" 被当作 FreeMarker 插值
            cfg.setInterpolationSyntax(freemarker.template.Configuration.DOLLAR_INTERPOLATION_SYNTAX);
        }
        return cfg;
    }

    @Override
    public List<Map<String, String>> previewFiles(GenTable table, List<GenTableColumn> columns) {
        String basePkg = (table.getBasePackage() != null && !table.getBasePackage().isBlank()) ? table.getBasePackage() : "com.kexio";
        String module = table.getModuleName();
        String entity = table.getEntityName();
        String pkgDir = basePkg.replace('.', '/');
        List<Map<String, String>> files = new ArrayList<>();

        // 兜底：确保模板可用字段有默认值
        if (table.getBasePackage() == null || table.getBasePackage().isBlank()) {
            table.setBasePackage(basePkg);
        }
        if (table.getTemplateType() == null || table.getTemplateType().isBlank()) {
            table.setTemplateType("single");
        }

        // render with templates (严格模式：模板缺失直接抛错，不再使用任何兜底字符串)
        // v2.3.0: 移除文件路径中的重复module，现在basePackage已包含完整包路径
        
        // 根据 templateType 确定模板目录（单表 single / 一对多 multi）
        String templateTypeDir = Objects.equals(table.getTemplateType(), "multi") ? "multi" : "single";
        
        // Entity 和 Mapper 使用通用模板（无需区分）
        add(files, "entity", pkgDir + "/entity/" + entity + ".java",
            render("java/entity.java.ftl", table, columns));
        add(files, "mapper", pkgDir + "/mapper/" + entity + "Mapper.java",
            render("java/mapper.java.ftl", table, columns));
        
        // MapperXml 根据类型选择
        String defaultXmlTpl = Objects.equals(table.getTemplateType(), "multi") ? "mapperXml/multi.xml.ftl" : "mapperXml/single.xml.ftl";
        String mapperXmlContent = render(defaultXmlTpl, table, columns);
        add(files, "mapperXml", "resources/mapper/" + module + "/" + entity + "Mapper.xml", mapperXmlContent);
        
        // Service 接口使用通用模板
        add(files, "service", pkgDir + "/service/" + entity + "Service.java",
            render("java/service.java.ftl", table, columns));
        
        // ServiceImpl 根据类型选择模板
        add(files, "serviceImpl", pkgDir + "/service/impl/" + entity + "ServiceImpl.java",
            render("java/" + templateTypeDir + "/serviceImpl.java.ftl", table, columns));
        
        // Controller 根据类型选择模板
        add(files, "controller", pkgDir + "/controller/" + entity + "Controller.java",
            render("java/" + templateTypeDir + "/controller.java.ftl", table, columns));
        
        // DTO 根据类型选择模板
        add(files, "dto", pkgDir + "/dto/" + entity + "DTO.java",
            render("java/" + templateTypeDir + "/dto.java.ftl", table, columns));
        
        // VO 和 MapStruct 使用通用模板
        add(files, "vo", pkgDir + "/vo/" + entity + "VO.java",
            render("java/vo.java.ftl", table, columns));
        add(files, "mapstruct", pkgDir + "/mapstruct/" + entity + "MapStruct.java",
            render("java/mapstruct.java.ftl", table, columns));
        
        // 前端 API 根据类型选择模板
        add(files, "frontend:api", "src/api/" + module + "/" + entity + ".ts",
            render("frontend/" + templateTypeDir + "/api.ts.ftl", table, columns));
        
        // 前端 Vue 页面根据类型选择模板
        String lowerEntity = entity.substring(0,1).toLowerCase() + entity.substring(1);
        add(files, "frontend:page", "src/views/" + module + "/" + lowerEntity + "/index.vue",
            render("frontend/" + templateTypeDir + "/index.vue.ftl", table, columns));
        // SQL: menu & permissions
        String ts = new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        add(files, "sql", "resources/db/migration/V" + ts + "__" + module + "_" + entity + "_menu_permissions.sql",
            render("sql/menu-permission.sql.ftl", table, columns));
        return files;
    }

    @Override
    public String generate(GenTable table, List<GenTableColumn> columns) {
        List<Map<String, String>> files = previewFiles(table, columns);
        String module = table.getModuleName();
        String entity = table.getEntityName();
        boolean overwrite = Boolean.TRUE.equals(table.getOverwriteEnabled());

        String baseDir = table.getOutputDir();
        if (baseDir == null || baseDir.isBlank()) {
            File def = new File(System.getProperty("user.dir"), "kexio-gen-output/" + module + "/" + entity);
            baseDir = def.getAbsolutePath();
        }

        int generated = 0;
        int skipped = 0;
        for (Map<String, String> f : files) {
            String relPath = f.get("path");
            String content = f.get("content");
            if (relPath == null) continue;

            String target;
            if (relPath.startsWith("resources/")) {
                target = baseDir + File.separator + "src" + File.separator + "main" + File.separator + relPath;
            } else if (relPath.startsWith("src/")) {
                target = baseDir + File.separator + relPath;
            } else {
                target = baseDir + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + relPath;
            }

            File targetFile = new File(target);
            if (targetFile.exists() && !overwrite) {
                skipped++;
                continue;
            }
            FileUtil.mkParentDirs(targetFile);
            FileUtil.writeString(content == null ? "" : content, targetFile, StandardCharsets.UTF_8);
            generated++;
        }

        return "generated=" + generated + ", skipped=" + skipped + ", dir=" + baseDir;
    }

    private void add(List<Map<String, String>> files, String type, String path, String content) {
        Map<String, String> entry = new HashMap<>();
        entry.put("type", type);
        entry.put("path", path);
        entry.put("content", content);
        files.add(entry);
    }

    private String render(String templatePath, GenTable table, List<GenTableColumn> columns) {
        try {
            Map<String, Object> model = new HashMap<>();
            model.put("table", table);
            model.put("columns", columns);
            model.put("now", new Date());
            // 添加用户信息
            Map<String, Object> user = new HashMap<>();
            user.put("name", "系统");
            model.put("user", user);
            String entity = table.getEntityName();
            String lowerEntity = entity.substring(0,1).toLowerCase() + entity.substring(1);
            model.put("entityName", entity);
            model.put("lowerEntity", lowerEntity);
            model.put("permissionPrefix", Optional.ofNullable(table.getPermissionPrefix()).orElse(table.getModuleName() + ":" + lowerEntity));
            freemarker.template.Template t = cfg().getTemplate(templatePath);
            StringWriter sw = new StringWriter();
            t.process(model, sw);
            return sw.toString();
        } catch (Exception e) {
            throw new RuntimeException("模板渲染失败: " + templatePath + ", " + e.getMessage(), e);
        }
    }

    private String renderStrict(String path, GenTable table, List<GenTableColumn> columns) {
        Map<String, Object> model = new HashMap<>();
        model.put("table", table);
        model.put("columns", columns);
        model.put("now", new Date());
        // 添加用户信息
        Map<String, Object> user = new HashMap<>();
        user.put("name", "系统");
        model.put("user", user);
        String entity = table.getEntityName();
        String lowerEntity = entity.substring(0,1).toLowerCase() + entity.substring(1);
        model.put("entityName", entity);
        model.put("lowerEntity", lowerEntity);
        model.put("permissionPrefix", Optional.ofNullable(table.getPermissionPrefix()).orElse(table.getModuleName() + ":" + lowerEntity));
        freemarker.template.Template t = tryLoadTemplate(path);
        if (t == null) {
            throw new RuntimeException("模板未找到: " + path + "，请检查路径是否为 classpath:/ 资源或有效的文件系统路径");
        }
        try {
            StringWriter sw = new StringWriter();
            t.process(model, sw);
            return sw.toString();
        } catch (Exception e) {
            throw new RuntimeException("模板渲染失败: " + path + ", " + e.getMessage(), e);
        }
    }

    private freemarker.template.Template tryLoadTemplate(String path) {
        if (path == null || path.isBlank()) return null;
        // 优先用 cfg 既有加载器（templates/gen/ 相对路径）
        try {
            return cfg().getTemplate(path);
        } catch (Exception ignore) { }
        // classpath: 前缀（相对于 classpath 根）
        String cp = path;
        if (path.startsWith("classpath:")) {
            cp = path.substring("classpath:".length());
        }
        try {
            InputStream is = CodeGenServiceImpl.class.getClassLoader().getResourceAsStream(cp);
            if (is != null) {
                try (Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
                    return new freemarker.template.Template(cp, reader, cfg());
                }
            }
        } catch (Exception ignore) { }
        // 文件系统路径（绝对或工作目录相对）
        File f = new File(path);
        if (!f.isAbsolute()) {
            f = new File(System.getProperty("user.dir"), path);
        }
        if (f.exists() && f.isFile()) {
            try {
                String content = cn.hutool.core.io.FileUtil.readString(f, StandardCharsets.UTF_8);
                return new freemarker.template.Template(f.getName(), content, cfg());
            } catch (Exception ignore) { }
        }
        return null;
    }

    // pickTemplate 已不再需要

    // 已移除所有字符串兜底生成器，模板缺失将直接抛错，便于尽早发现问题
}



